import React, { Component } from 'react'
import PropTypes from 'prop-types'
import style from './Tree.module.less'
import _ from 'loadsh'
import { MinusOutlined, PlusOutlined } from '@ant-design/icons'

export default class Tree extends Component {
  state = {
    treeData: [],
    col: 0,
    row: 0
  }

  formatColAndRow (list = [], isInitial = false) {
    if (list.length === 0) {
      return { col: 1, row: 1 }
    }
    const rows = []
    const cols = []
    list.forEach(li => {
      if (isInitial) {
        li.childVisible = true
      }
      if (!li.childVisible) {
        cols.push(1)
        rows.push(1)
        li.col = 1
        li.row = 1
        return false
      }
      const obj = this.formatColAndRow(li.children, isInitial)
      const col = obj.col
      const row = obj.row
      li.col = col
      li.row = row
      cols.push(col)
      rows.push(row)
    })
    return {
      col: _.max(cols) + 1,
      row: _.sum(rows)
    }
  }

  initData () {
    const d = _.assignIn([], this.props.data)
    const { col, row } = this.formatColAndRow(d, true)
    this.setState({
      treeData: d,
      col,
      row
    })
  }

  // 切换树节点的展开状态
  toggleNode (e, keys) {
    e.preventDefault()
    e.stopPropagation()
    const arr = [...this.props.data]
    let obj = {}
    let list = arr
    keys.forEach(key => {
      for (let i = 0; i < list.length; i++) {
        if (list[i].key === key) {
          obj = list[i]
          list = obj.children
        }
      }
    })
    if (obj) {
      obj.childVisible = !obj.childVisible
      const { col, row } = this.formatColAndRow(arr)
      this.setState({
        treeData: arr,
        col,
        row
      })
    }
  }

  // 限制文本长度
  limitLen (s) {
    if (!s) {
      return s
    }
    return s.length > 30 ? s.slice(0, 30) + '...' : s
  }

  componentDidUpdate (prev) {
    if (prev.data !== this.props.data) {
      this.initData()
    }
  }

  componentDidMount () {
    this.initData()
  }

  renderNode (list, pKey = []) {
    const { nodeWidth, nodeHeight } = this.props
    const width = nodeWidth + 40
    const height = nodeHeight + 20
    return list.map((li, i) => {
      const keys = [...pKey, li.key]
      return (
        <div key={li.key} className={style.box}>
          <div className={style['left-box']} style={{ width: width + 'px', height: height * li.row + 'px' }}>
            {i === 0 ? <div className={style['left-box-top-bg']} /> : null}
            {i === list.length - 1 ? <div className={style['left-box-bottom-bg']} /> : null}
            <div className={style['left-node']} style={{ width: nodeWidth + 'px' }} onClick={() => this.props.clickNode(li.key, li, keys)}>
              {this.renderNodeContent(li)}
              {li.children.length ? (
                <div className={style['toggle-btn']} onClick={(e) => this.toggleNode(e, keys)}>
                  {li.childVisible ? <MinusOutlined /> : <PlusOutlined />}
                </div>
              ) : null}
            </div>
          </div>
          {
            li.children.length && li.childVisible ? (
              <div className={style['right-box']} style={{ width: width * (li.col - 1) + 'px' }}>
                {this.renderNode(li.children, keys)}
              </div>
            ) : null
          }
        </div>
      )
    })
  }

  // 渲染树节点内容
  renderNodeContent (li) {
    if (this.props.renderNode) {
      return this.props.renderNode(li)
    }
    return <span>{this.limitLen(li.name)}</span>
  }

  render () {
    const { nodeWidth, nodeHeight } = this.props
    const width = nodeWidth + 40
    const height = nodeHeight + 20
    const { col, row } = this.state
    return (
      <div style={{ width: width * (col - 1) + 'px', height: height * row + 'px' }}>
        {this.renderNode(this.state.treeData)}
      </div>
    )
  }
}

Tree.propTypes = {
  data: PropTypes.array,
  nodeWidth: PropTypes.number,
  nodeHeight: PropTypes.number,
  renderNode: PropTypes.func,
  clickNode: PropTypes.func
}

Tree.defaultProps = {
  data: [],
  nodeWidth: 350,
  nodeHeight: 60,
  renderNode: null,
  clickNode: () => {}
}
